package io.zerous.engine

import android.view.MotionEvent
import android.view.View
import io.zerous.engine.utils.Pool

/**
 * Created by Zerous on 2018/2/4.
 */
class TouchHandler(view: View) : View.OnTouchListener {
    private val touchEventPool = Pool(10) {
        TouchEvent()
    }

    val isTouched = BooleanArray(MAX_TOUCHPOINTS)
    val touchX = IntArray(MAX_TOUCHPOINTS)
    val touchY = IntArray(MAX_TOUCHPOINTS)
    val id = IntArray(MAX_TOUCHPOINTS)

    val touchEvents = ArrayList<TouchEvent>()
    val touchEventsBuffer = ArrayList<TouchEvent>()

    init {
        view.setOnTouchListener(this)
    }

    override fun onTouch(view: View, event: MotionEvent): Boolean {
        synchronized(this) {
            val action = event.action and MotionEvent.ACTION_MASK
            val pointerIndex = (event.action and MotionEvent.ACTION_POINTER_ID_MASK) shr MotionEvent.ACTION_POINTER_ID_SHIFT
            val pointerCount = event.pointerCount

            var touchEvent: TouchEvent
            for (i in 0 until MAX_TOUCHPOINTS) {
                if (i >= pointerCount) {
                    isTouched[i] = false
                    id[i] = -1
                    continue
                }
                val pointerId = event.getPointerId(i)
                if (event.action != MotionEvent.ACTION_MOVE && i != pointerIndex) {
                    continue
                }

                when(action) {
                    MotionEvent.ACTION_DOWN,
                    MotionEvent.ACTION_POINTER_DOWN -> {
                        touchEvent = touchEventPool.newObject()
                        touchEvent.type = TOUCH_DOWN
                        touchEvent.pointer = pointerId
                        touchEvent.x = event.getX(i).toInt()
                        touchX[i] = touchEvent.x
                        touchEvent.y = event.getY(i).toInt()
                        touchY[i] = touchEvent.y
                        isTouched[i] = true
                        id[i] = pointerId
                        touchEventsBuffer.add(touchEvent)
                    }

                    MotionEvent.ACTION_UP,
                    MotionEvent.ACTION_POINTER_UP,
                    MotionEvent.ACTION_CANCEL -> {
                        touchEvent = touchEventPool.newObject()
                        touchEvent.type = TOUCH_UP
                        touchEvent.pointer = pointerId
                        touchEvent.x = event.getX(i).toInt()
                        touchX[i] = touchEvent.x
                        touchEvent.y = event.getY(i).toInt()
                        touchY[i] = touchEvent.y
                        isTouched[i] = false
                        id[i] = -1
                        touchEventsBuffer.add(touchEvent)
                    }

                    MotionEvent.ACTION_MOVE -> {
                        touchEvent = touchEventPool.newObject()
                        touchEvent.type = TOUCH_DRAGGED
                        touchEvent.pointer = pointerId
                        touchEvent.x = event.getX(i).toInt()
                        touchX[i] = touchEvent.x
                        touchEvent.y = event.getY(i).toInt()
                        touchY[i] = touchEvent.y
                        isTouched[i] = true
                        id[i] = pointerId
                        touchEventsBuffer.add(touchEvent)
                    }
                }
            }
        }
        return true
    }

    fun isTouchDown(pointer: Int): Boolean {
        synchronized(this) {
            val index = getIndex(pointer)
            if (index < 0 || index >= MAX_TOUCHPOINTS) {
                return false
            } else {
                return isTouched[index]
            }
        }
    }

    fun getTouchX(pointer: Int): Int {
        synchronized(this) {
            val index = getIndex(pointer)
            if (index < 0 || index >= MAX_TOUCHPOINTS)
                return -1
            else
                return touchX[index]
        }
    }

    fun getTouchY(pointer: Int): Int {
        synchronized(this) {
            val index = getIndex(pointer)
            if (index < 0 || index >= MAX_TOUCHPOINTS)
                return -1
            else
                return touchY[index]
        }
    }

    fun getTouchEvents(): List<TouchEvent> {
        synchronized(this) {
            for (i in touchEvents.indices)
                touchEventPool.free(touchEvents[i])

            touchEvents.clear()
            touchEvents.addAll(touchEventsBuffer)
            touchEventsBuffer.clear()
            return touchEvents
        }
    }

    private fun getIndex(pointerId: Int): Int {
        for (i in 0 until MAX_TOUCHPOINTS) {
           if (id[i] == pointerId)
               return i
        }
        return -1
    }
}